class Fragment {
    constructor(text, values) {
        this.raw = text;
        this.values = values;
    }
}

function cond(k, v, chain) {
    let type = typeof v;
  
    if(Array.isArray(v) && v.length == 0)
      return chain([], "FALSE");
    if(type == "object")
      return chain([], `${k} IS${v === null ? '' : ' NOT'} NULL`);
    if(type == "boolean")
      return chain([], '%sNOT(%s)');
  
    return chain([v], '%s=?:');
  }

function merge(vals, operator, chain) {
    let conds = [], data = [];
  
    vals.forEach(function(val) {
      where(val, function(vals, txt) {
        data.push.apply(data, vals);
        conds.push(txt);
      });
    });
    return chain(data, conds.length == 1 ? conds[0] : '(' + conds.join(operator) + ')');
  }

function where(vals, chain) {

    let type = typeof vals;

    if (vals instanceof Fragment)
        return chain(vals.values, vals.raw);

    if (Array.isArray(vals) && vals.length == 0 || vals === undefined || vals === null)
        return chain([], "FALSE");

    if (type == "boolean")
        return chain([], (vals ? "TRUE" : "FALSE"));

    if (Array.isArray(vals))
        return merge(vals, ' AND ', chain);

    if (type == "object") {
        let conds = [], data = [];
        for (let k in vals) {
            cond(k, vals[k], function (vals, txt) {
                data.push.apply(data, vals);
                conds.push(txt);
            });
        }
        return chain(data, conds.join(' AND '));
    }
    return chain([], vals);
}

console.log(where(true, function (data, txt) {
    data +  `a WHERE ${txt}`;
}))
const values = [];
values.push("banana", "apple", "peach");
console.log(merge(values, 'and', function (data, txt) {
    data +  `a WHERE ${txt}`;
}))
console.log(cond('a', values, function (data, txt) {
    data +  `a WHERE ${txt}`;
}))
